#include "sfc_cpu.h"
#include "log.h"

/*
CPU memory map (From NESDoc Figure 2-3)
Address range	Device
[$0000, $2000)	CPU internal RAM, 2KB avaliable, 6KB mirroring
[$2000, $4020)	I/O Registers
[$4020, $6000)	Expansion ROM
[$6000, $8000)	SRAM
[$8000, $10000)	PRG-ROM
*/

uint8_t sfc_read_cpu_address(uint16_t address, const sfc_famicom_t* famicom) {
	// LOG("read mem, addr: $%x\n", address);

	// 判断地址最高三位
	switch (address >> 13) {
	case 0x0:
		// [$0000, $2000), CPU internal mem， 做了 4 次 mirroring
		return famicom->main_memory[address & (uint16_t)0x07ff];
	case 0x1:
	case 0x2:
		// [$2000, $6000), 未实现
		LOG_FATAL("access not impl address: %x, abort", address);
		return 0;
	case 0x3:
		// [$6000, $8000) SRAM
		return famicom->save_memory[address & (uint16_t)0x1fff];
	case 0x4:
	case 0x5:
	case 0x6:
	case 0x7:
		// [$8000, $10000) PRG-ROM
		return famicom->prg_banks[address >> 13][address & (uint16_t)0x1fff];
	}
	LOG_FATAL("access invalid address:%x, abort", address);
}

void sfc_write_cpu_address(uint16_t address, uint8_t data, sfc_famicom_t* famicom) {
	// LOG("write mem, addr: %x\n", address);

	// 判断地址最高三位
	switch (address >> 13) {
	case 0x0:
		// [$0000, $2000), CPU internal mem， 做了 4 次 mirroring
		famicom->main_memory[address & (uint16_t)0x07ff] = data;
		return;
	case 0x1:
	case 0x2:
		// [$2000, $6000), 未实现
		LOG_FATAL("access not impl address: %x, abort", address);
		return;
	case 0x3:
		// [$6000, $8000) SRAM
		famicom->save_memory[address & (uint16_t)0x1fff] = data;
		return;
	case 0x4:
	case 0x5:
	case 0x6:
	case 0x7:
		// [$8000, $10000) PRG-ROM
		LOG_FATAL("write a read-only area, addr: %x\n", address);
		// famicom->prg_banks[address >> 13][address & (uint16_t)0x1fff] = data;
		return;
	}
	LOG_FATAL("access invalid address:%x, abort", address);
	return;
}